package com.promis.crypto.asymmetric;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.Cipher;

public class RSAKeyCipher extends AsymmetricKeyCipher {
	
	private static final int MAX_ENCRYPT_BLOCK = 117;
	
	private static final int MAX_DECRYPT_BLOCK = 128;
	
	private final static String KEY_ALGORITHM = "RSA";
	
	private final static String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
	
	private static RSAKeyCipher keyCipher;

	private RSAKeyCipher() {
		super(KEY_ALGORITHM, CIPHER_ALGORITHM);
	}
	
	public synchronized static RSAKeyCipher getInstance() {
		if (keyCipher == null) {
			keyCipher = new RSAKeyCipher();
		}
		return keyCipher;
	}
	
	@Override
	public byte[] encryptByPublicKey(byte[] data, PublicKey publicKey) throws Exception {
		return encrypt(data, publicKey);
	}
	
	@Override
	public byte[] decryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
		return decrypt(data, privateKey);
	}
	
	@Override
	public byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {  
		return encrypt(data, privateKey);
	}
	
	@Override
	public byte[] decryptByPublicKey(byte[] data, PublicKey publicKey) throws Exception {
		return decrypt(data, publicKey);
	}
	
	private byte[] encrypt(byte[] data, Key key) throws Exception {
		Cipher cipher = Cipher.getInstance(cipherAlgorithm);  
		cipher.init(Cipher.ENCRYPT_MODE, key);  
		
		int inputLen = data.length;
		byte[] encryptedData = null;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
	        int offSet = 0;  
	        byte[] cache;  
	        int i = 0;  
	        // 对数据分段加密  
	        while (inputLen - offSet > 0) {  
	            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {  
	                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);  
	            } else {  
	                cache = cipher.doFinal(data, offSet, inputLen - offSet);  
	            }  
	            out.write(cache, 0, cache.length);  
	            i++;  
	            offSet = i * MAX_ENCRYPT_BLOCK;  
	        }  
	        encryptedData = out.toByteArray();  
		} finally {
			out.close();
		}
		return encryptedData;
	}
	
	private byte[] decrypt(byte[] data, Key key) throws Exception {
		Cipher cipher = Cipher.getInstance(cipherAlgorithm);  
		cipher.init(Cipher.DECRYPT_MODE, key);  
		
		int inputLen = data.length;
		byte[] decryptedData = null;
		int offSet = 0;  
        byte[] cache;  
        int i = 0; 
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			while (inputLen - offSet > 0) {  
	            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {  
	                cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);  
	            } else {  
	                cache = cipher.doFinal(data, offSet, inputLen - offSet);  
	            }  
	            out.write(cache, 0, cache.length);  
	            i++;  
	            offSet = i * MAX_DECRYPT_BLOCK;  
	        }  
	        decryptedData = out.toByteArray();
		} finally {
			out.close();
		}
		return decryptedData;
	}
}